package org.reliablesource.prism.gef.editor;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.EventObject;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.gef.ContextMenuProvider;
import org.eclipse.gef.DefaultEditDomain;
import org.eclipse.gef.EditPartViewer;
import org.eclipse.gef.GraphicalViewer;
import org.eclipse.gef.SharedImages;
import org.eclipse.gef.dnd.TemplateTransferDragSourceListener;
import org.eclipse.gef.dnd.TemplateTransferDropTargetListener;
import org.eclipse.gef.editparts.ScalableFreeformRootEditPart;
import org.eclipse.gef.palette.PaletteRoot;
import org.eclipse.gef.requests.CreationFactory;
import org.eclipse.gef.requests.SimpleFactory;
import org.eclipse.gef.ui.actions.ActionRegistry;
import org.eclipse.gef.ui.palette.FlyoutPaletteComposite;
import org.eclipse.gef.ui.palette.FlyoutPaletteComposite.FlyoutPreferences;
import org.eclipse.gef.ui.parts.ContentOutlinePage;
import org.eclipse.gef.ui.parts.GraphicalEditorWithFlyoutPalette;
import org.eclipse.gef.ui.parts.GraphicalViewerKeyHandler;
import org.eclipse.gef.ui.parts.TreeViewer;
import org.eclipse.jface.util.TransferDropTargetListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.part.IPageSite;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
import org.reliablesource.prism.core.IPrismScreen;
import org.reliablesource.prism.gef.editor.model.ScreenAdapter;
import org.reliablesource.prism.gef.editor.parts.PrismEditPartFactory;
import org.reliablesource.prism.gef.editor.parts.PrismTreeEditPartFactory;


/**
 * @author Dennis Park
 *
 */
public class PrismScreenEditor extends GraphicalEditorWithFlyoutPalette implements PropertyChangeListener {

	public static final String ID = "org.reliablesource.prism.gef.editor.PrismScreenEditor";
	
	private static PaletteRoot PALETTE_MODEL;

	public static PrismScreenEditor instance;

	public static Image pointerImage;
	
	private GraphicalViewer viewer;
	
	public PrismScreenEditor() {
		super();
		setEditDomain(new DefaultEditDomain(this));
		pointerImage = SharedImages.DESC_SELECTION_TOOL_24.createImage();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.gef.ui.parts.GraphicalEditorWithPalette#getPaletteRoot()
	 */
	
	protected PaletteRoot getPaletteRoot() {
		if (PALETTE_MODEL == null)
			PALETTE_MODEL = PrismScreenEditorPaletteFactory.createPalette();
		return PALETTE_MODEL;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.gef.ui.parts.GraphicalEditor#initializeGraphicalViewer()
	 */
	
	protected void initializeGraphicalViewer() {
		super.initializeGraphicalViewer();
		viewer = getGraphicalViewer();
		viewer.setContents(getModel()); // set the contents of this editor
		// listen for dropped parts
		viewer.addDropTargetListener(createTransferDropTargetListener());
		
	}
	
	/**
	 * Create a transfer drop target listener. When using a CombinedTemplateCreationEntry
	 * tool in the palette, this will enable model element creation by dragging from the palette.
	 * @see #createPaletteViewerProvider()
	 */
	private TransferDropTargetListener createTransferDropTargetListener() {
		return new TemplateTransferDropTargetListener(getGraphicalViewer()) {
			protected CreationFactory getFactory(Object template) {
				return new SimpleFactory((Class) template);
			}
		};
	}
	
	protected ScreenAdapter getModel() {
		return ((PrismScreenEditorInput) getEditorInput()).getInput();
	}
	
	public Object getAdapter(final Class type) {
		if (type.getClass().equals(IContentOutlinePage.class))
			return new PrismOutlinePage(new TreeViewer());
		return super.getAdapter(type);
	}

	/**
	 * Configure the graphical viewer before it receives contents.
	 * <p>This is the place to choose an appropriate RootEditPart and EditPartFactory
	 * for your editor. The RootEditPart determines the behavior of the editor's "work-area".
	 * For example, GEF includes zoomable and scrollable root edit parts. The EditPartFactory
	 * maps model elements to edit parts (controllers).</p>
	 * @see org.eclipse.gef.ui.parts.GraphicalEditor#configureGraphicalViewer()
	 */
	protected void configureGraphicalViewer() {
		super.configureGraphicalViewer();
		
		GraphicalViewer viewer = getGraphicalViewer();
		viewer.setEditPartFactory(new PrismEditPartFactory());
		viewer.setRootEditPart(new ScalableFreeformRootEditPart());
		viewer.setKeyHandler(new GraphicalViewerKeyHandler(viewer));
		
		// configure the context menu provider
		ContextMenuProvider cmProvider =
				new PrismScreenEditorContextMenuProvider(viewer, getActionRegistry());
		viewer.setContextMenu(cmProvider);
		viewer.addDragSourceListener(new TemplateTransferDragSourceListener(viewer));
		getSite().registerContextMenu(cmProvider, viewer);
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.ui.part.EditorPart#doSave(org.eclipse.core.runtime.IProgressMonitor)
	 */
	
	public void doSave(IProgressMonitor monitor) {

	}

	/* (non-Javadoc)
	 * @see org.eclipse.ui.part.EditorPart#doSaveAs()
	 */
	
	public void doSaveAs() {
	}

	public void init(IEditorSite site, IEditorInput input) throws PartInitException{
		super.init(site, input);
		getModel().getScreen().addPropertyChangeListener(this);
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.ui.part.EditorPart#isSaveAsAllowed()
	 */
	
	public boolean isSaveAsAllowed() {
		return false;
	}

	
	protected FlyoutPreferences getPalettePreferences() {
		return new FlyoutPreferences(){

			public int getDockLocation() {
				return 0;
			}

			public int getPaletteState() {
				return FlyoutPaletteComposite.STATE_PINNED_OPEN;
			}

			public int getPaletteWidth() {
				return 120;
			}

			public void setDockLocation(int location) {
				
			}

			public void setPaletteState(int state) {
				
			}

			public void setPaletteWidth(int width) {
				
			}
			
		};
	}
	
	public void commandStackChanged(EventObject event) {
		firePropertyChange(IEditorPart.PROP_DIRTY);
		super.commandStackChanged(event);
	}
	
	public void setFocus(){
		super.setFocus();
		PrismScreenEditor.instance = this;
	}

	public void propertyChange(PropertyChangeEvent evt) {
		String prop = evt.getPropertyName();
		if(prop.equals(IPrismScreen.IMAGE_PATH)){
			try{
				getGraphicalViewer().setContents(this.getModel());
			}
			catch(Exception ex){
				// image path not set.
			}
		}		
	}
	
	/**
	 * Creates an outline pagebook for this editor.
	 */
	public class PrismOutlinePage extends ContentOutlinePage {	
		/**
		 * Create a new outline page for the shapes editor.
		 * @param viewer a viewer (TreeViewer instance) used for this outline page
		 * @throws IllegalArgumentException if editor is null
		 */
		public PrismOutlinePage(EditPartViewer viewer) {
			super(viewer);
		}

		/* (non-Javadoc)
		 * @see org.eclipse.ui.part.IPage#createControl(org.eclipse.swt.widgets.Composite)
		 */
		public void createControl(Composite parent) {
			// create outline viewer page
			getViewer().createControl(parent);
			// configure outline viewer
			getViewer().setEditDomain(getEditDomain());
			getViewer().setEditPartFactory(new PrismTreeEditPartFactory());
			// configure & add context menu to viewer
			ContextMenuProvider cmProvider = new PrismScreenEditorContextMenuProvider(
					getViewer(), getActionRegistry()); 
			getViewer().setContextMenu(cmProvider);
//			getSite().registerContextMenu(
//					"org.eclipse.gef.examples.shapes.outline.contextmenu",
//					cmProvider, getSite().getSelectionProvider());		
			// hook outline viewer
			getSelectionSynchronizer().addViewer(getViewer());
			// initialize outline viewer with model
			getViewer().setContents(getModel());
			// show outline viewer
		}
		
		/* (non-Javadoc)
		 * @see org.eclipse.ui.part.IPage#dispose()
		 */
		public void dispose() {
			// unhook outline viewer
			getSelectionSynchronizer().removeViewer(getViewer());
			// dispose
			pointerImage.dispose();
			super.dispose();
		}

		/* (non-Javadoc)
		 * @see org.eclipse.ui.part.IPage#getControl()
		 */
		public Control getControl() {
			return getViewer().getControl();
		}
		
		/**
		 * @see org.eclipse.ui.part.IPageBookViewPage#init(org.eclipse.ui.part.IPageSite)
		 */
		public void init(IPageSite pageSite) {
			super.init(pageSite);
			ActionRegistry registry = getActionRegistry();
			IActionBars bars = pageSite.getActionBars();
			String id = ActionFactory.UNDO.getId();
			bars.setGlobalActionHandler(id, registry.getAction(id));
			id = ActionFactory.REDO.getId();
			bars.setGlobalActionHandler(id, registry.getAction(id));
			id = ActionFactory.DELETE.getId();
			bars.setGlobalActionHandler(id, registry.getAction(id));
		}
	}
	
	public static void setPointerLocationToStatusLine(Point location){
		instance.getEditorSite().getActionBars().getStatusLineManager().setMessage(pointerImage, "Current Pointer Location: " + location.toString());
	}

	/**
	 * @return Returns the viewer.
	 */
	public GraphicalViewer getGfxViewer() {
		return viewer;
	}
}
